/**
 * Copyright (c) 2015-2030 by Beijing Yanlight Co., Ltd.
 * All rights reserved.
 */
package com.yanlight.support;

import com.yanlight.exception.ErrorType;
import com.yanlight.exception.TradeException;
import com.yanlight.util.DateTimeUtil;
import com.yanlight.util.ResponseUtil;
import com.yanlight.util.SessionUtil;
import com.yanlight.util.WebUtil;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author haolw
 * @time 2017/08/18
 */
public class PretreatmentFilter implements Filter {
    private Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());
    private PathMatcher pathMatcher;
    private List<String> notAuthUrls;
    private final String ASSETS_PREFIX = "/assets/**";
    private DefaultServletHttpRequestHandler resourceHandler;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String servletPath = request.getServletPath();
        String contextPath = request.getContextPath();

        request.setAttribute("ctx", contextPath);
        boolean process = true;
        try {
            if (assets(servletPath)) {
                process = false;
                resourceHandler.handleRequest(request, response);
            } else {
                if (authUrl(servletPath)) {
                    if (necessaryLogin(request, response)) {
                        process = false;
                    }
                }
            }
            if (process) {
                chain.doFilter(request, response);
            }
        } catch (Exception e) {
            doException(request, response, e);
        }
    }

    /**
     * 判断时候为静态资源
     *
     * @param path
     * @return
     */
    protected boolean assets(String path) {
        return pathMatcher.match(ASSETS_PREFIX, path);
    }

    /**
     * 判断是否为需要登录认证相关地址
     *
     * @param path
     * @return
     */
    protected boolean authUrl(String path) {
        return !notAuthUrl(path);
    }

    /**
     * 判断是否为不需要登录认证相关地址
     *
     * @param path
     * @return
     */
    protected boolean notAuthUrl(String path) {
        return pathMatch(path, notAuthUrls);
    }

    /**
     * 匹配地址
     *
     * @param path
     * @param list
     * @return
     */
    protected boolean pathMatch(String path, List<String> list) {
        for (String res : list) {
            if (pathMatcher.match(res, path)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 检测是否登录
     *
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    protected boolean necessaryLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        HttpSession session = request.getSession();
        Integer userId = SessionUtil.getUserId(session);
        if (userId == null) {
            internalRedirectLogin(request, response);
            return true;
        }
        return false;
    }

    /**
     * 跳转到登录页
     *
     * @param request
     * @param response
     * @throws IOException
     */
    protected void internalRedirectLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // api请求返回
        if (WebUtil.apiRequest(request)) {
            ErrorType errorType = ErrorType.USER_NOT_LOGIN;
            Integer code = errorType.getCode();
            String msg = errorType.getZhCnMsg();
            WebUtil.renderJson(response, ResponseUtil.fail(code, msg));
            return;
        }

        // 处理项目路径
        String servletPath = request.getServletPath();
        String contextPath = request.getContextPath();
        if (!contextPath.endsWith("/")) {
            contextPath += "/";
        }

        // 组装重定向url
        String redirectUrl = contextPath;
        if ("".equals(servletPath) || "/".equals(servletPath)) {
            redirectUrl += "login";
        } else {
            redirectUrl += "login-relay";
        }
        response.sendRedirect(redirectUrl);
        return;
    }

    /**
     * @param request
     * @param response
     * @param e
     */
    protected void doException(HttpServletRequest request, HttpServletResponse response, Exception e) {
        ErrorType errorType = null;
        Throwable rootException = ExceptionUtils.getRootCause(e);
        if (rootException instanceof TradeException) {
            TradeException ex = (TradeException) rootException;
            errorType = ex.getErrorType();
            logException(rootException, errorType);
        } else {
            e.printStackTrace();
            errorType = ErrorType.SYSTEM_ERROR;
        }
        if (WebUtil.apiRequest(request)) {
            Integer code = errorType.getCode();
            String msg = errorType.getZhCnMsg();
            WebUtil.renderJson(response, ResponseUtil.fail(code, msg));
            return;
        } else {
            throw new RuntimeException(e);
        }
    }

    /**
     * @param throwable
     * @param errorType
     */
    protected void logException(Throwable throwable, ErrorType errorType) {
        String className = throwable.getStackTrace()[1].getClassName();
        String methodName = throwable.getStackTrace()[1].getMethodName();
        int lineNumbe = throwable.getStackTrace()[1].getLineNumber();
        String nowTime = DateTimeUtil.getNowTime();
        Integer code = errorType.getCode();
        String enMsg = errorType.getEnMsg();
        String zhCnMsg = errorType.getZhCnMsg();
        String classLog = "{} exception at:{}.{}:{}";
        String typeLog = "{} errorType:{code:{},enMsg:{},cnMsg:{}}";
        logger.error(classLog, nowTime, className, methodName, lineNumbe);
        logger.error(typeLog, nowTime, code, enMsg, zhCnMsg);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ServletContext context = filterConfig.getServletContext();
        resourceHandler = new DefaultServletHttpRequestHandler();
        resourceHandler.setServletContext(context);
        pathMatcher = new AntPathMatcher();
        notAuthUrls = new ArrayList<String>(8);
        notAuthUrls.add("/verify/get");
        notAuthUrls.add("/login");
        notAuthUrls.add("/login-relay");
        notAuthUrls.add("/user/login");

        notAuthUrls.add("/wx/upload");
        notAuthUrls.add("/wx/apply");
        notAuthUrls.add("/wx/applyStatus");
        notAuthUrls.add("/wx/applyList");
        notAuthUrls.add("/wx/updateSubMchId");

        notAuthUrls.add("/appManager/payCodeInfo");
        notAuthUrls.add("/appManager/updatePayCode");
        notAuthUrls.add("/appManager/companyInfo");


//        notAuthUrls.add("/company/delete");
        notAuthUrls.add("/order/drawbackResponse/{orderId}");


    }

    @Override
    public void destroy() {

    }

}
